package com.jun.rabbitmq.producer.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author yuejun.li
 * @date 2024/4/23 11:03:21
 * 生产者配置类
 */
@Slf4j
@Configuration
public class ProducerConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback{

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory,MessageConverter messageConverter) {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter);
        // 当mandatory设置为true时，若exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息，那么broker会调用basic.return方法将消息返还给生产者。
        // 当mandatory设置为false时，出现上述情况broker会直接将消息丢弃。
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
        return rabbitTemplate;
    }

    /**
     * 消息只要被 RabbitMQ broker 接收到就会触发confirm方法
     * @param correlationData 对象内部有id（消息的唯一性）和Message。（若ack为false，则Message不为null，可将Message数据 重新投递；若ack是true，则correlationData为null）
     * @param ack 消息投递到exchange的状态，true-成功|false-失败
     * @param cause 表示投递失败的原因。（若ack为false，则cause不为null；若ack是true，则cause为null）
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (!ack) {
            log.error("confirm==>发送到broker失败\r\n" +
                            "correlationData={}\r\n" + "ack={}\r\n" + "cause={}",
                    correlationData, ack, cause);
            //可以添加一个向对应队列重新发送的逻辑
        } else {
            log.info("confirm==>发送到broker成功\r\n" +
                            "correlationData={}\r\n" + "ack={}\r\n" + "cause={}",
                    correlationData, ack, cause);
        }
    }

    //如果消息未能投递到目标queue里将触发returnedMessage方法
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        log.error("触发return callback\r\n" +
                "交换机: {}\r\n" + "路由key: {}\r\n" + "消息体: {}\r\n" + "响应code: {}\r\n" + "响应内容: {}",
                returned.getExchange(),returned.getRoutingKey(),returned.getMessage(),returned.getReplyCode(),returned.getReplyText());
    }
}
